package com.xm.security.util;

import com.xm.security.core.MD5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * 描述: 获取系统参数
 *
 * @author chen-guipeng
 * @date 2022/8/3
 */
public class SerialNumberUtil {

    protected static final Logger log = LoggerFactory.getLogger(SerialNumberUtil.class);

    /**
     * 获取主板序列号（windows）
     *
     * @return
     */
    public static String getMotherboardSN() {
        String result = "";
        try {
            File file = File.createTempFile("realhowto", ".vbs");
            file.deleteOnExit();
            FileWriter fw = new FileWriter(file);
            String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
                    + "Set colItems = objWMIService.ExecQuery _ \n"
                    + " (\"Select * from Win32_BaseBoard\") \n"
                    + "For Each objItem in colItems \n"
                    + " Wscript.Echo objItem.SerialNumber \n"
                    + " exit for ' do the first cpu only! \n" + "Next \n";
            fw.write(vbs);
            fw.close();
            String path = file.getPath().replace("%20", " ");
            Process p = Runtime.getRuntime().exec(
                    "cscript //NoLogo " + path);
            BufferedReader input = new BufferedReader(new InputStreamReader(
                    p.getInputStream()));
            String line;
            while ((line = input.readLine()) != null) {
                result += line;
            }
            input.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.trim();
    }


    /**
     * 获取硬盘序列号(windows)
     * 该方法获取的是 盘符的逻辑序列号,并不是硬盘本身的序列号
     *
     * @param drive 盘符
     * @return
     */
    public static String getHardDiskSN(String drive) {
        String result = "";
        try {
            File file = File.createTempFile("realhowto", ".vbs");
            file.deleteOnExit();
            FileWriter fw = new FileWriter(file);
            String vbs = "Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\n"
                    + "Set colDrives = objFSO.Drives\n"
                    + "Set objDrive = colDrives.item(\""
                    + drive
                    + "\")\n"
                    + "Wscript.Echo objDrive.SerialNumber"; // see note
            fw.write(vbs);
            fw.close();
            String path = file.getPath().replace("%20", " ");
            Process p = Runtime.getRuntime().exec(
                    "cscript //NoLogo " + path);
            BufferedReader input = new BufferedReader(new InputStreamReader(
                    p.getInputStream()));
            String line;
            while ((line = input.readLine()) != null) {
                result += line;
            }
            input.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.trim();
    }


    /**
     * 获取CPU序列号（windows）
     *
     * @return
     */
    public static String getCPUSerial() {
        String result = "";
        try {
            File file = File.createTempFile("tmp", ".vbs");
            file.deleteOnExit();
            FileWriter fw = new FileWriter(file);
            String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
                    + "Set colItems = objWMIService.ExecQuery _ \n"
                    + " (\"Select * from Win32_Processor\") \n"
                    + "For Each objItem in colItems \n"
                    + " Wscript.Echo objItem.ProcessorId \n"
                    + " exit for ' do the first cpu only! \n" + "Next \n";
// + " exit for \r\n" + "Next";
            fw.write(vbs);
            fw.close();
            String path = file.getPath().replace("%20", " ");
            Process p = Runtime.getRuntime().exec(
                    "cscript //NoLogo " + path);
            BufferedReader input = new BufferedReader(new InputStreamReader(
                    p.getInputStream()));
            String line;
            while ((line = input.readLine()) != null) {
                result += line;
            }
            input.close();
            file.delete();
        } catch (Exception e) {
            e.fillInStackTrace();
        }
        if (result.trim().length() < 1 || result == null) {
            result = "无CPU_ID被读取";
        }
        return result.trim();
    }

    private static List<String> getLocalHostLANAddress() throws SocketException {
        List<String> ips = new ArrayList<>();
        Enumeration<NetworkInterface> interfs = NetworkInterface.getNetworkInterfaces();
        while (interfs.hasMoreElements()) {
            NetworkInterface interf = interfs.nextElement();
            Enumeration<InetAddress> addres = interf.getInetAddresses();
            while (addres.hasMoreElements()) {
                InetAddress in = addres.nextElement();
                if (in instanceof Inet4Address) {
                    System.out.println("v4:" + in.getHostAddress());
                    if (!"127.0.0.1".equals(in.getHostAddress())) {
                        ips.add(in.getHostAddress());
                    }
                }
            }
        }
        return ips;
    }

    /**
     * MAC（windows）
     * 通过jdk自带的方法,先获取本机所有的ip,然后通过NetworkInterface获取mac地址
     *
     * @return
     */
    public static String getMac() {
        try {
            String resultStr = "";
            List<String> ls = getLocalHostLANAddress();
            for (String str : ls) {
                InetAddress ia = InetAddress.getByName(str);// 获取本地IP对象
                // 获得网络接口对象（即网卡），并得到mac地址，mac地址存在于一个byte数组中。
                byte[] mac = NetworkInterface.getByInetAddress(ia)
                        .getHardwareAddress();
                // 下面代码是把mac地址拼装成String
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < mac.length; i++) {
                    if (i != 0) {
                        sb.append("-");
                    }
                    // mac[i] & 0xFF 是为了把byte转化为正整数
                    String s = Integer.toHexString(mac[i] & 0xFF);
                    sb.append(s.length() == 1 ? 0 + s : s);
                }
                // 把字符串所有小写字母改为大写成为正规的mac地址并返回
                resultStr += sb.toString().toUpperCase() + ",";
            }
            return resultStr;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /***************************linux*********************************/
    public static String executeLinuxCmd(String cmd) {
        log.info("运行linux命令：" + cmd);
        try {
            System.out.println("got cmd job : " + cmd);
            Runtime run = Runtime.getRuntime();
            Process process;
            process = run.exec(cmd);
            InputStream in = process.getInputStream();
            BufferedReader bs = new BufferedReader(new InputStreamReader(in));
            StringBuffer out = new StringBuffer();
            byte[] b = new byte[8192];
            for (int n; (n = in.read(b)) != -1; ) {
                out.append(new String(b, 0, n));
            }
            in.close();
            process.destroy();
            return out.toString();
        } catch (Exception e) {
            log.error("运行linux命令异常信息：", e);
        }
        return null;
    }

    /**
     * 获取系统序列号(Linux)
     *
     * @param cmd     命令语句
     * @param records 要查看的字段集合
     * @return
     */
    public static List<String> getSerialNumber(String cmd, List<String> records) {
        String execResult = executeLinuxCmd(cmd);
        log.info("运行linux命令：" + cmd + " 结果：" + execResult);
        log.info("要查看的属性集合：" + records);
        String[] infos = execResult.split("\n");
        List<String> result = new ArrayList<>();
        for (String record : records) {
            for (String info : infos) {
                info = info.trim();
                if (info.indexOf(record) != -1) {
                    result.add(info.replace(record, "").replaceAll(" ", ""));
                    break;
                }
            }
        }
        return result;
    }

    /**
     * 获取CPUID+硬盘序列号
     *
     * @return
     */
    public static String getCPUAndDisk(String drive) {
        String os = System.getProperty("os.name");
        os = os.toUpperCase();
        log.info("系统：" + os);
        String cpuid = "";
        String diskSN = "";
        if ("LINUX".equals(os)) {
            List<String> attribute = new ArrayList<>();
            attribute.add("ID:");
            List<String> cpu = getSerialNumber("dmidecode -t processor | grep 'ID'", attribute);
            if (cpu.size() > 0) {
                cpuid = cpu.get(0);
            }
            List<String> attribute2 = new ArrayList<>();
            attribute2.add("Disk identifier:");
            attribute2.add("磁盘标识符：");
            List<String> disk = getSerialNumber("fdisk -l", attribute2);
            if (disk.size() > 0) {
                diskSN = disk.get(0);
            }
        } else if (os.contains("WINDOW")) {
            cpuid = getCPUSerial();
            diskSN = getHardDiskSN(drive);
        }
        log.info("CPU序列号：" + cpuid + "，硬盘序列号：" + diskSN);
        return cpuid + diskSN;
    }

    public static void main(String[] args) throws Exception {
        String sn = getCPUAndDisk("D");
        System.out.println(sn);
        System.out.println(MD5.digest.encodeString(sn));
    }

}
